package com.jic.market.impl.rest.discountamount;

import com.alibaba.fastjson.JSON;
import com.jic.market.common.exception.MarketErrorCodeEnum;
import com.jic.market.common.exception.MarketException;
import com.jic.market.enums.DiscountAmountServiceOperateEnum;
import com.jic.market.enums.DiscountOrderStatusEnum;
import com.jic.market.enums.FrontPromotionTypeEnum;
import com.jic.market.enums.PromotionTypeEnum;
import com.jic.market.impl.rest.discountamount.model.DiscountAmountModel;
import com.jic.market.impl.rest.discountamount.model.DiscountOrderModel;
import com.jic.market.impl.rest.discountamount.model.PromotionAmountModel;
import com.jic.market.impl.rest.discountamount.po.GoodsPo;
import com.jic.market.impl.rest.discountamount.processor.AmountProcessor;
import com.jic.market.request.rest.discountamount.*;
import com.jic.market.response.rest.discountamount.GiftDetail;
import com.jic.market.response.rest.discountamount.*;
import com.jic.market.response.rest.groupon.LaunchOrJoinGrouponResponse;
import com.jic.market.response.rest.groupon.PostPayGroupInstDetailInfo;
import com.jic.market.service.rest.discountamount.DiscountAmountModelService;
import com.jic.market.service.rest.discountamount.DiscountAmountService;
import com.jic.market.service.rest.discountamount.PromotionGoodsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author yinhaijun
 * @date: 2020/4/7
 */
@Slf4j
@Service
public class DiscountAmountServiceImpl extends ApplicationObjectSupport implements DiscountAmountService {

    @Autowired
    private PromotionGoodsService promotionGoodsService;
    @Autowired
    private DiscountAmountModelService discountAmountModelService;
    private static final HashMap<FrontPromotionTypeEnum, String> processorMap;

    static {
        processorMap = new HashMap<>();
        FrontPromotionTypeEnum[] values = FrontPromotionTypeEnum.values();
        for(FrontPromotionTypeEnum value : values){
            if(null != value.getProcessor() && !value.getProcessor().equals("")){
                processorMap.put(value, value.getProcessor());
            }
        }
    }

    @Override
    public ShoppingCartDiscountAmountQueryResponse queryShoppingCartDiscount(@Valid ShoppingCartDiscountAmountQueryRequest request) {
        log.info("查询购物车优惠金额入参{}", JSON.toJSONString(request));
        request.check();
        // 构建模型
        Map<Long, GoodsPo> goodsPoMap = promotionGoodsService.buildGoodsPoMap(request.getPromotionGoodsList());
        List<DiscountAmountModel> discountAmountModelList = discountAmountModelService.build(goodsPoMap, request.getCouponCodeList(), null, null, FrontPromotionTypeEnum.getByCode(request.getFrontPromotionType()));
        for(DiscountAmountModel discountAmountModel : discountAmountModelList){
            // 处理业务
            process(discountAmountModel, goodsPoMap.get(discountAmountModel.getSkuId()), request.getThirdUserId(), request, DiscountAmountServiceOperateEnum.SHOPPING_CART_QUERY);
        }
        // 移除优惠金额为0的数据
        for(int i = discountAmountModelList.size()-1; i>=0 ;i--){
            if(discountAmountModelList.get(i).getDiscountTotalAmount() == null || discountAmountModelList.get(i).getDiscountTotalAmount().compareTo(BigDecimal.ZERO) == 0){
                discountAmountModelList.remove(discountAmountModelList.get(i));
            }
        }
        // 返回数据
        ShoppingCartDiscountAmountQueryResponse response = (ShoppingCartDiscountAmountQueryResponse) buildResult(discountAmountModelList, new ShoppingCartDiscountAmountQueryResponse() ,DiscountAmountServiceOperateEnum.SHOPPING_CART_QUERY);
        return response;
    }

    /**
     * 整体思路如下：
     * // 获取到对应的活动商品记录
     * // 对活动商品记录进行分类，按活动id分，因为一个活动下面可能有多件商品
     * // 查询商品的叠加规则
     * // 如果不存在叠加规则，则直接计算即可，如果是两个叠加，如果是三个叠加，如果是多个叠加规则同时存在取最新创建时间的叠加规则
     * // 叠加规则中分为满额减和非满额减
     * // 满额减情况：使用原价进行计算，最后把各个活动优惠金额相加，
     * // 非满额减情况：首先进行活动优先级排序，然后使用上次活动计算的结果进行带入下一次活动计算，如果是同类活动（比如两张优惠卷）则按满额减来处理
     * @param request
     * @return
     */
    @Override
    public DiscountAmountQueryResponse queryDiscount(DiscountAmountQueryRequest request) {
        log.info("查询优惠金额入参{}", JSON.toJSONString(request));
        request.check();
        // 构建模型
        Map<Long, GoodsPo> goodsPoMap = promotionGoodsService.buildGoodsPoMap(request.getPromotionGoodsList());
        List<DiscountAmountModel> discountAmountModelList = discountAmountModelService.build(goodsPoMap, request.getCouponCodeList(), request.getGiftMap(), null, FrontPromotionTypeEnum.getByCode(request.getFrontPromotionType()));
        for(DiscountAmountModel discountAmountModel : discountAmountModelList){
            // 处理业务
            process(discountAmountModel, goodsPoMap.get(discountAmountModel.getSkuId()), request.getThirdUserId(), request, DiscountAmountServiceOperateEnum.SETTLEMENT_QUERY);
        }
        // 返回数据
        DiscountAmountQueryResponse response = buildResult(discountAmountModelList, new DiscountAmountQueryResponse() ,DiscountAmountServiceOperateEnum.SETTLEMENT_QUERY);
        log.info("查询优惠金额出参{}", JSON.toJSONString(response));
        return response;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized DiscountAmountQueryResponse createDiscount(@Valid DiscountAmountOrderCreateRequest request) {
        log.info("优惠金额创建订单入参{}", JSON.toJSONString(request));
        request.check();
        // 校验
        discountAmountModelService.check(request.getOrderNo(), DiscountAmountServiceOperateEnum.CREATE);
        // 构建模型
        Map<Long, GoodsPo> goodsPoMap = promotionGoodsService.buildGoodsPoMap(request.getPromotionGoodsList());
        List<DiscountAmountModel> discountAmountModelList = discountAmountModelService.build(goodsPoMap, request.getCouponCodeList(), request.getGiftMap(), null, FrontPromotionTypeEnum.getByCode(request.getFrontPromotionType()));
        for(DiscountAmountModel discountAmountModel : discountAmountModelList){
            // 处理业务
            process(discountAmountModel, goodsPoMap.get(discountAmountModel.getSkuId()), request.getThirdUserId(), request, DiscountAmountServiceOperateEnum.CREATE);
        }
        DiscountOrderModel discountOrderModel = new DiscountOrderModel();
        discountOrderModel.setOrderNo(request.getOrderNo());
        discountOrderModel.setOrderCreateTime(request.getOrderCreateTime());
        discountOrderModel.setDiscountOrderStatus(DiscountOrderStatusEnum.CREATE);
        discountOrderModel.setDiscountAmountModelList(discountAmountModelList);
        discountOrderModel.setThirdUserId(request.getThirdUserId());
        // 保存数据
        discountAmountModelService.save(discountOrderModel, request.getAdminOperate());
        // 返回数据
        DiscountAmountQueryResponse response = (DiscountAmountQueryResponse) buildResult(discountAmountModelList, new DiscountAmountQueryResponse() ,DiscountAmountServiceOperateEnum.CREATE);
        log.info("优惠金额创建订单出参{}", JSON.toJSONString(response));
        return response;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized DiscountAmountQueryResponse payDiscount(@Valid DiscountAmountOrderPayRequest request) {
        // 校验
        discountAmountModelService.check(request.getOrderNo(), DiscountAmountServiceOperateEnum.PAY);
        DiscountOrderModel discountOrderModel = discountAmountModelService.build(request.getOrderNo(), null);
        discountOrderModel.setOrderPayTime(request.getOrderPayTime());
        discountOrderModel.setPayAmount(request.getPayAmount());
        discountOrderModel.setDiscountOrderStatus(DiscountOrderStatusEnum.PAY);
        List<DiscountAmountModel> discountAmountModelList = discountOrderModel.getDiscountAmountModelList();
        if(null != discountAmountModelList && !discountAmountModelList.isEmpty()){
            process(discountAmountModelList, DiscountAmountServiceOperateEnum.PAY, request.getOrderNo(),request);
        }

        discountAmountModelService.save(discountOrderModel, request.getAdminOperate());
        // 返回数据
        DiscountAmountQueryResponse response = (DiscountAmountQueryResponse) buildResult(discountAmountModelList, new DiscountAmountQueryResponse() ,DiscountAmountServiceOperateEnum.PAY);

        return response;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized DiscountRefundResponse refundDiscount(@Valid DiscountAmountRefundRequest request) {
        // 校验
        discountAmountModelService.check(request.getOrderNo(), DiscountAmountServiceOperateEnum.REFUND);
        DiscountOrderModel discountOrderModel = discountAmountModelService.build(request.getOrderNo(), request.getThirdProductCodeList());
        discountOrderModel.setDiscountOrderStatus(DiscountOrderStatusEnum.REFUND);
        List<DiscountAmountModel> discountAmountModelList = discountOrderModel.getDiscountAmountModelList();
        if(null != discountAmountModelList && !discountAmountModelList.isEmpty()){
            process(discountAmountModelList, DiscountAmountServiceOperateEnum.REFUND, request.getOrderNo(), request);
        }

        discountAmountModelService.save(discountOrderModel, request.getAdminOperate());
        // 返回数据
        DiscountRefundResponse response = (DiscountRefundResponse) buildResult(discountAmountModelList, new DiscountRefundResponse() ,DiscountAmountServiceOperateEnum.REFUND);
        return response;
    }

    @Override
    public DiscountAmountQueryResponse queryDiscount(@Valid GrouponDiscountAmountQueryRequest request) {
        // 构建模型
        Map<Long, GoodsPo> goodsPoMap = promotionGoodsService.buildGoodsPoMap(request.getPromotionGoodsList());
        List<DiscountAmountModel> discountAmountModelList = discountAmountModelService.build(goodsPoMap, null, null, request.getPromotionCode(), FrontPromotionTypeEnum.getByCode(request.getFrontPromotionType()));
        for(DiscountAmountModel discountAmountModel : discountAmountModelList){
            // 处理业务
            process(discountAmountModel, goodsPoMap.get(discountAmountModel.getSkuId()), request.getThirdUserId(), request, DiscountAmountServiceOperateEnum.SETTLEMENT_QUERY);
        }
        // 返回数据
        DiscountAmountQueryResponse response = buildResult(discountAmountModelList, new DiscountAmountQueryResponse() ,DiscountAmountServiceOperateEnum.SETTLEMENT_QUERY);
        log.info("查询拼团优惠金额出参{}", JSON.toJSONString(response));
        return response;
    }

    @Override
    public GrouponDiscountAmountOrderCreateResponse createGrouponDiscount(@Valid GrouponDiscountAmountOrderCreateRequest request) {
        // 构建模型
        Map<Long, GoodsPo> goodsPoMap = promotionGoodsService.buildGoodsPoMap(request.getPromotionGoodsList());
        List<DiscountAmountModel> discountAmountModelList = discountAmountModelService.build(goodsPoMap, request.getCouponCodeList(), null, request.getPromotionCode(), FrontPromotionTypeEnum.getByCode(request.getFrontPromotionType()));
        for(DiscountAmountModel discountAmountModel : discountAmountModelList){
            // 处理业务
            process(discountAmountModel, goodsPoMap.get(discountAmountModel.getSkuId()), request.getThirdUserId(), request, DiscountAmountServiceOperateEnum.CREATE);
        }
        DiscountOrderModel discountOrderModel = new DiscountOrderModel();
        discountOrderModel.setOrderNo(request.getOrderNo());
        discountOrderModel.setOrderCreateTime(request.getOrderCreateTime());
        discountOrderModel.setDiscountOrderStatus(DiscountOrderStatusEnum.CREATE);
        discountOrderModel.setDiscountAmountModelList(discountAmountModelList);
        discountOrderModel.setThirdUserId(request.getThirdUserId());
        // 保存数据
        discountAmountModelService.save(discountOrderModel, request.getAdminOperate());
        // 返回数据
        GrouponDiscountAmountOrderCreateResponse response = (GrouponDiscountAmountOrderCreateResponse) buildResult(discountAmountModelList, new GrouponDiscountAmountOrderCreateResponse() ,DiscountAmountServiceOperateEnum.CREATE);
        log.info("创建拼团优惠金额出参{}", JSON.toJSONString(response));
        return response;
    }


    private DiscountAmountQueryResponse buildResult(List<DiscountAmountModel> discountAmountModelList, DiscountAmountQueryResponse discountAmountQueryResponse, DiscountAmountServiceOperateEnum discountAmountServiceOperateEnum) {
        if(null != discountAmountModelList && !discountAmountModelList.isEmpty()){
            BigDecimal allDiscountTotalAmount = BigDecimal.ZERO;
            List<SkuDiscountDetail> skuDiscountDetailList = new ArrayList<>();
            for(DiscountAmountModel discountAmountModel : discountAmountModelList){
                allDiscountTotalAmount = allDiscountTotalAmount.add(discountAmountModel.getDiscountTotalAmount());
                SkuDiscountDetail skuDiscountDetail = new SkuDiscountDetail();
                skuDiscountDetail.setSkuId(discountAmountModel.getSkuId());
                skuDiscountDetail.setThirdProductCode(discountAmountModel.getThirdSkuId());
                List<PromotionDiscountDetail> promotionDiscountDetailList = new ArrayList<>();
                DiscountAmountModel.ExclusiveGroup exclusiveGroup = discountAmountModel.getExclusiveGroup();
                if(null != exclusiveGroup){
                    List<PromotionAmountModel> exclist = discountAmountModel.getExclusiveGroup().getPromotionAmountModelList();
                    convertResultPromotionList(discountAmountServiceOperateEnum, promotionDiscountDetailList, exclist, discountAmountQueryResponse);
                }
                DiscountAmountModel.OverlayGroup overlayGroup = discountAmountModel.getOverlayGroup();
                if(null != overlayGroup){
                    List<PromotionAmountModel> ovelist = discountAmountModel.getOverlayGroup().getPromotionAmountModelList();
                    convertResultPromotionList(discountAmountServiceOperateEnum, promotionDiscountDetailList, ovelist, discountAmountQueryResponse);
                }
                skuDiscountDetail.setPromotionDiscountDetailList(promotionDiscountDetailList);
                skuDiscountDetailList.add(skuDiscountDetail);
            }
            discountAmountQueryResponse.setDiscountTotalAmount(allDiscountTotalAmount);
            discountAmountQueryResponse.setSkuDiscountDetailList(skuDiscountDetailList);
            return discountAmountQueryResponse;
        }
        return null;
    }

    private void convertResultPromotionList(DiscountAmountServiceOperateEnum discountAmountServiceOperateEnum, List<PromotionDiscountDetail> promotionDiscountDetailList, List<PromotionAmountModel> exclist, DiscountAmountQueryResponse discountAmountQueryResponse) {
        if(null != exclist && !exclist.isEmpty()){
            exclist.forEach(exc ->{
                PromotionDiscountDetail promotionDiscountDetail = new PromotionDiscountDetail();
                promotionDiscountDetail.setPromotionId(exc.getPromotionId());
                promotionDiscountDetail.setPromotionCode(exc.getPromotionCode());
                promotionDiscountDetail.setFrontPromotionType(exc.getFrontPromotionTypeEnum().getCode());
                promotionDiscountDetail.setFrontPromotionTypeName(exc.getFrontPromotionTypeEnum().getMessage());
                promotionDiscountDetail.setPromotionDiscountTotalAmount(exc.getPromotionDiscountTotalAmount());
                promotionDiscountDetailList.add(promotionDiscountDetail);
                if(exc.getFrontPromotionTypeEnum().getPromType() == PromotionTypeEnum.GIFT && DiscountAmountServiceOperateEnum.SHOPPING_CART_QUERY == discountAmountServiceOperateEnum){
                    if(null != exc.getRuleData()){
                        List<GiftDetail> giftDetailList = (List<GiftDetail>) exc.getRuleData();
                        ShoppingCartDiscountAmountQueryResponse response = (ShoppingCartDiscountAmountQueryResponse) discountAmountQueryResponse;
                        if(null == response.getGiftMap()){
                            Map<String, List<GiftDetail>> giftMap = new HashMap<>();
                            response.setGiftMap(giftMap);
                        }
                        response.getGiftMap().put(exc.getPromotionCode(), giftDetailList);
                    }
                }
                if(exc.getFrontPromotionTypeEnum().getPromType() == PromotionTypeEnum.GROUPON && DiscountAmountServiceOperateEnum.REFUND == discountAmountServiceOperateEnum){
                    if(null != exc.getRuleData()){
                        List<PostPayGroupInstDetailInfo> detailInfoList = (List<PostPayGroupInstDetailInfo>) exc.getRuleData();
                        DiscountRefundResponse response = (DiscountRefundResponse) exc.getRuleData();
                        response.setDetailInfoList(detailInfoList);
                    }
                }
                if(exc.getFrontPromotionTypeEnum().getPromType() == PromotionTypeEnum.GROUPON && DiscountAmountServiceOperateEnum.CREATE == discountAmountServiceOperateEnum){
                    if(null != exc.getRuleData()) {
                        LaunchOrJoinGrouponResponse launchOrJoinGrouponResponse = (LaunchOrJoinGrouponResponse) exc.getRuleData();
                        GrouponDiscountAmountOrderCreateResponse grouponDiscountAmountOrderCreateResponse = (GrouponDiscountAmountOrderCreateResponse) discountAmountQueryResponse;
                        grouponDiscountAmountOrderCreateResponse.setGrouponInstCode(launchOrJoinGrouponResponse.getGrouponInstCode());
                        grouponDiscountAmountOrderCreateResponse.setLaunchOrJoinSuccess(launchOrJoinGrouponResponse.getLaunchOrJoinSuccess());
                    }
                }
            });
        }
    }


    private void process(DiscountAmountModel discountAmountModel, GoodsPo goodsPo, String thirdUserId, Object request, DiscountAmountServiceOperateEnum discountAmountServiceOperate) {
        DiscountAmountModel.ExclusiveGroup exclusiveGroup = discountAmountModel.getExclusiveGroup();
        DiscountAmountModel.OverlayGroup overlayGroup = discountAmountModel.getOverlayGroup();
        // 该商品未参与任何活动,优惠总金额为0
        if(null == exclusiveGroup && null == overlayGroup){
            discountAmountModel.setDiscountTotalAmount(BigDecimal.ZERO);
        }
        // 互斥活动
        if(null != exclusiveGroup && null != exclusiveGroup.getPromotionAmountModelList()
                &&!exclusiveGroup.getPromotionAmountModelList().isEmpty()){
            for(PromotionAmountModel promotionAmountModel : exclusiveGroup.getPromotionAmountModelList()){
                calculation(promotionAmountModel.getFrontPromotionTypeEnum(), promotionAmountModel, goodsPo, thirdUserId, request, discountAmountServiceOperate);
            }
            // 取最大优惠
            DiscountAmountModel.maxDiscount(exclusiveGroup);
        }
        // 叠加活动
        if(null != overlayGroup && null != overlayGroup.getPromotionAmountModelList()
            && !overlayGroup.getPromotionAmountModelList().isEmpty()){
            // 满额减
            if(overlayGroup.getFullAmountReduceFlag().equals("1")) {
                for (PromotionAmountModel promotionAmountModel : overlayGroup.getPromotionAmountModelList()) {
                    calculation(promotionAmountModel.getFrontPromotionTypeEnum(), promotionAmountModel, goodsPo, thirdUserId, request, discountAmountServiceOperate);
                }
            // 非满额减
            }else {
                BigDecimal prevPromotionAvailableTotalPrice = BigDecimal.ZERO;
                BigDecimal prevJoinTotalAmount = BigDecimal.ZERO;
                FrontPromotionTypeEnum prevFrontPromotionType = null;
                for (PromotionAmountModel promotionAmountModel : overlayGroup.getPromotionAmountModelList()) {
                    // 如果两个相邻的类型不一样，则把计算后的促销价作为下一次计算的基准价，如果一样，则把上一次的joinTotalAmount用作计算
                    if(null != prevFrontPromotionType){
                        if(prevFrontPromotionType != promotionAmountModel.getFrontPromotionTypeEnum()){
                            promotionAmountModel.setJoinTotalAmount(prevPromotionAvailableTotalPrice);
                        }else {
                            promotionAmountModel.setJoinTotalAmount(prevJoinTotalAmount);
                        }
                    }
                    calculation(promotionAmountModel.getFrontPromotionTypeEnum(), promotionAmountModel, goodsPo, thirdUserId, request, discountAmountServiceOperate);
                    prevPromotionAvailableTotalPrice = new BigDecimal(promotionAmountModel.getPromotionAvailableTotalPrice().doubleValue());
                    prevJoinTotalAmount = new BigDecimal(promotionAmountModel.getJoinTotalAmount().doubleValue());
                    prevFrontPromotionType = promotionAmountModel.getFrontPromotionTypeEnum();
                }
            }
            // 优惠金额求和
            DiscountAmountModel.sumDiscount(overlayGroup);
        }

        // 互斥和叠加取最大优惠金额
        DiscountAmountModel.maxDiscount(discountAmountModel);
    }

    private void calculation(FrontPromotionTypeEnum frontPromotionTypeEnum, PromotionAmountModel promotionAmountModel, GoodsPo goodsPo, String thirdUserId, Object request, DiscountAmountServiceOperateEnum discountAmountServiceOperate){
        ApplicationContext applicationContext = getApplicationContext();
        String processorName = processorMap.get(frontPromotionTypeEnum);
        if(null == processorName || "".equals(processorName)){
            promotionAmountModel.fillNoDiscount(goodsPo);
            return;
        }
        AmountProcessor amountProcessor = (AmountProcessor)applicationContext.getBean(processorName);
        if(DiscountAmountServiceOperateEnum.SETTLEMENT_QUERY == discountAmountServiceOperate){
            amountProcessor.query(promotionAmountModel, goodsPo, thirdUserId, request);
        }
        else if(DiscountAmountServiceOperateEnum.CREATE == discountAmountServiceOperate){
            amountProcessor.create(promotionAmountModel, goodsPo, thirdUserId, request);
        }
        else if(DiscountAmountServiceOperateEnum.SHOPPING_CART_QUERY == discountAmountServiceOperate){
            amountProcessor.shoppingQuery(promotionAmountModel, goodsPo, thirdUserId, request);
        }
    }

    private void process(List<DiscountAmountModel> discountAmountModelList, DiscountAmountServiceOperateEnum discountAmountServiceOperate, String orderNo, Object request) {
        for (DiscountAmountModel discountAmountModel : discountAmountModelList) {
            DiscountAmountModel.ExclusiveGroup exclusiveGroup = discountAmountModel.getExclusiveGroup();
            // 处理业务
            if (null != exclusiveGroup && null != exclusiveGroup.getPromotionAmountModelList() && !exclusiveGroup.getPromotionAmountModelList().isEmpty()) {
                List<PromotionAmountModel> promotionAmountModelList = exclusiveGroup.getPromotionAmountModelList();
                promotionAmountModelList.forEach(promotionAmountModel -> {
                    ApplicationContext applicationContext = getApplicationContext();
                    String processorName = processorMap.get(promotionAmountModel.getFrontPromotionTypeEnum());
                    if (null == processorName || "".equals(processorName)) {
                        throw new MarketException(MarketErrorCodeEnum.SYS_ERROR, "不支持的活动类型");
                    }
                    AmountProcessor amountProcessor = (AmountProcessor) applicationContext.getBean(processorName);
                    if(DiscountAmountServiceOperateEnum.PAY == discountAmountServiceOperate){
                        amountProcessor.pay(promotionAmountModel);
                    }
                    else if(DiscountAmountServiceOperateEnum.REFUND == discountAmountServiceOperate){
                        amountProcessor.refund(promotionAmountModel, orderNo, request);
                    }
                });
            }
        }
    }

}
